module AboutYou
  module SDK
    module Model
      module CategoryManager
        ###
        # This class is responsible for handling all the logic when working with
        # categories
        #
        # author:: Collins GmbH & Co KG
        ###
        class DefaultCategoryManager
          # the duration for the cached values to live
          DEFAULT_CACHE_DURATION = 7200

          # the read-in categories from the category manager
          attr_accessor :categories
          # the parent child ids for the categories
          attr_accessor :parent_child_ids
          # the cache client
          attr_accessor :cache
          # the cache key for the app
          attr_accessor :cache_key
          # instance of AY
          attr_accessor :shop_api

          ###
          # the Constructor for the AY class
          #
          # * *Args*    :
          #   - +cache+ -> The cache client set on the AY class
          #   - +app_id+ -> The App-Id of the App
          #   - +shop_api+ -> an Instance of AY
          #
          # * *Returns* :
          #   - Instance of AboutYou::SDK::Model::CategoryManager::DefaultCategoryManager
          ###
          def initialize(cache = nil, app_id = '', shop_api)
            self.categories = {}
            self.shop_api = shop_api
            self.cache    = cache
            self.cache_key = 'AY:SDK:' + String(app_id) + ':categories'
            load_cached_categories
          end

          ###
          # Gets the cached Categories for this app
          #
          # * *Returns* :
          #   - True/False determining whether the loading was sucessfull or not
          ###
          def load_cached_categories
            parse_json(
              cache.get(cache_key),
              shop_api.model_factory_instance
            ) if cache && cache.exists(cache_key)
          end

          ###
          # this method caches a given json_object
          #
          # * *Args*    :
          #   - +json_object+ -> the jsonObject received from the api
          #
          # * *Returns* :
          #   - True/False determining whether the setting was sucessfull or not
          ###
          def cache_categories(json_object)
            cache.set(cache_key, json_object, DEFAULT_CACHE_DURATION)
          end

          ###
          # This method clears the cache
          #
          # * *Returns* :
          #   - True/False determining whether the clearing was sucessfull or not
          ###
          def clear_cache
            cache.delete(cache_key)
          end

          ###
          # this method parses a json object received from the api and
          # creates models from it
          #
          # * *Args*    :
          #   - +json_object+ -> the jsonObject received from the api
          #   - +factory+ -> the model factory used for creating the models
          #
          # * *Returns* :
          #   - Instance of AboutYou::SDK::Model::CategoryManager::DefaultCategoryManager
          ###
          def parse_json(json_object, factory)
            return if !json_object && categories.empty?
            cache_categories(json_object) if cache
            self.parent_child_ids = json_object['parent_child']
            json_object['ids'].each do |id, json_category|
              categories[id] = factory.create_category(json_category)
            end
          end

          ###
          # this method checks whether this category manager has read-in
          # categories or not
          #
          # * *Returns* :
          #   - a boolean which determines whether this Category Manager has read-in categories or not
          ###
          def empty?
            categories.empty?
          end

          ###
          # This method returns the root categories for the read-in categories
          #
          # * *Args*    :
          #   - +activeOnly+ -> determines whether the result should contain only active categories or not
          #
          # * *Returns* :
          #   - a Hash containing pairs of Category_Id => instance of AboutYou::SDK::Model::Category
          ###
          def category_tree(active_only = AboutYou::SDK::Model::Category::ACTIVE_ONLY)
            subcategories(0, active_only)
          end

          ###
          # This method is used for getting a category model by a given id
          #
          # * *Args*    :
          #   - +id+ -> the id for which a category model should be returned
          #
          # * *Returns* :
          #   - either an instance of AboutYou::SDK::Model::Category or nil if +id+ is not found
          ###
          def category(id)
            return nil unless @categories[String(id)]
            @categories[String(id)]
          end

          ###
          # This method is used for getting either all categories when +id+
          # is nil or a Hash of all Category Models found for an Array of
          # Category Ids
          #
          # * *Args*    :
          #   - +ids+ -> an Array of category Ids
          #   - +activeOnly+ -> determines whether the result should contain only active categories or not
          #
          # * *Returns* :
          #   - a Hash containing pairs of Category_ID => an Instance of AboutYou::SDK::Model::Category
          ###
          def categories(
              ids = nil,
              active_only = AboutYou::SDK::Model::Category::ACTIVE_ONLY
            )
            return @categories if ids.nil?
            return {} unless @categories

            categories = {}

            ids.each do |id|
              next unless category(id)
              category = category(id)
              next unless
                active_only == AboutYou::SDK::Model::Category::ALL ||
                category.is_active
              categories[id] = category
            end

            categories
          end

          ###
          # This method gets the subcategories for a given category id
          #
          # * *Args*    :
          #   - +id+ -> an Array of category Ids
          #   - +activeOnly+ -> determines whether the result should contain only active categories or not
          #
          # * *Returns* :
          #   - a Hash containing pairs of Category_ID => an Instance of AboutYou::SDK::Model::Category
          ###
          def subcategories(id, active_only = AboutYou::SDK::Model::Category::ACTIVE_ONLY)
            return [] unless parent_child_ids && parent_child_ids.key?(String(id))
            categories(parent_child_ids[String(id)], active_only)
          end

          ###
          # This method gets the first category which has a certain name
          #
          # * *Args*    :
          #   - +name+ -> the name which should be found
          #   - +activeOnly+ -> determines whether the result should contain only active categories or not
          #
          # * *Returns* :
          #   - either an instance of AboutYou::SDK::Model::Category or nil if +name+ not found
          ###
          def first_category_by_name(
              name,
              active_only = AboutYou::SDK::Model::Category::ACTIVE_ONLY
            )
            categories.each do |category|
              return category if
                category.name == name &&
                (active_only == AboutYou::SDK::Model::Category::ACTIVE_ONLY ||
                category.is_active)
            end

            nil
          end

          ###
          # This method gets the subcategories for a given category id
          #
          # * *Args*    :
          #   - +id+ -> an Array of category Ids
          #   - +activeOnly+ -> determines whether the result should contain only active categories or not
          #
          # * *Returns* :
          #   - a Hash containing pairs of Category_ID => an Instance of AboutYou::SDK::Model::Category
          ###
          def categories_by_name(
              name,
              active_only = AboutYou::SDK::Model::Category::ACTIVE_ONLY
            )
            result_categories = []
            categories.each do |category|
              result_categories.push(category) if
              category.name == name &&
              (active_only == AboutYou::SDK::Model::Category::ALL ||
              category.is_active)
            end

            result_categories
          end
        end
      end
    end
  end
end
